#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <getopt.h>
#include <ctype.h>

#define OTA_HEADER_SIZE 32
#define MAX_FIRMWARE_TYPE_LEN 10

// 定义OTA文件头结构
typedef struct {
    unsigned char identifier1;
    unsigned char identifier2;
    unsigned char firmware_count;
    unsigned char major_version;
    unsigned char minor_version;
    unsigned char reserved[11];
    int firmware_length;
    unsigned int checksum;
    char firmware_type[MAX_FIRMWARE_TYPE_LEN];
} OTAHeader;

// 从hex文件转换为bin文件
void hex_to_bin(const char *input_file, const char *output_file) {
    // 这里可以调用hex2bin.exe工具，需要确保该工具在系统路径中
    char command[256];
    snprintf(command, sizeof(command), "hex2bin.exe %s %s", input_file, output_file);
    system(command);
}

// 从cly文件转换为bin文件
void cly_to_bin(const char *input_file, const char *output_file) {
    FILE *in = fopen(input_file, "r");
    FILE *out = fopen(output_file, "wb");
    if (in == NULL || out == NULL) {
        perror("Failed to open file");
        return;
    }

    // 读取程序长度
    char length_str[9];
    fgets(length_str, sizeof(length_str), in);
    int length = (int)strtol(length_str, NULL, 16);

    // 读取后续每行数据
    char line[257];
    while (fgets(line, sizeof(line), in) != NULL) {
        for (int i = 0; i < 128; i += 2) {
            char byte_str[3];
            strncpy(byte_str, line + i, 2);
            byte_str[2] = '\0';
            unsigned char byte = (unsigned char)strtol(byte_str, NULL, 16);
            fwrite(&byte, 1, 1, out);
        }
    }

    fclose(in);
    fclose(out);
}

// 计算文件的校验和
unsigned int calculate_checksum(const char *file_path) {
    FILE *file = fopen(file_path, "rb");
    if (file == NULL) {
        perror("Failed to open file");
        return 0;
    }

    unsigned int checksum = 0;
    unsigned char byte;
    while (fread(&byte, 1, 1, file) == 1) {
        checksum += byte;
    }

    fclose(file);
    return checksum;
}

// 将镜像文件打包为OTA升级包
void pack_image(const char *input_file, const char *firmware_type, const char *version) {
    // 处理输入文件格式
    char temp_bin_file[256];
    const char *extension = strrchr(input_file, '.');
    if (extension != NULL) {
        if (strcmp(extension, ".hex") == 0) {
            snprintf(temp_bin_file, sizeof(temp_bin_file), "%s.bin", input_file);
            hex_to_bin(input_file, temp_bin_file);
        } else if (strcmp(extension, ".cly") == 0) {
            snprintf(temp_bin_file, sizeof(temp_bin_file), "%s.bin", input_file);
            cly_to_bin(input_file, temp_bin_file);
        } else if (strcmp(extension, ".bin") == 0) {
            strcpy(temp_bin_file, input_file);
        } else {
            fprintf(stderr, "Unsupported file format\n");
            return;
        }
    }

    // 计算固件长度和校验和
    FILE *bin_file = fopen(temp_bin_file, "rb");
    if (bin_file == NULL) {
        perror("Failed to open file");
        return;
    }
    fseek(bin_file, 0, SEEK_END);
    int firmware_length = ftell(bin_file);
    fseek(bin_file, 0, SEEK_SET);
    unsigned int checksum = calculate_checksum(temp_bin_file);

    // 解析版本号
    unsigned char major_version = (unsigned char)atoi(strtok((char *)version, "."));
    unsigned char minor_version = (unsigned char)atoi(strtok(NULL, "."));

    // 创建OTA文件头
    OTAHeader header;
    header.identifier1 = 0xAA;
    header.identifier2 = 0x55;
    header.firmware_count = 1;
    header.major_version = major_version;
    header.minor_version = minor_version;
    memset(header.reserved, 0, sizeof(header.reserved));
    header.firmware_length = firmware_length;
    header.checksum = checksum;
    strncpy(header.firmware_type, firmware_type, MAX_FIRMWARE_TYPE_LEN);

    // 生成OTA文件名
    char ota_file[256];
    char *dot = strrchr(input_file, '.');
    if (dot != NULL) {
        *dot = '\0';
    }
    snprintf(ota_file, sizeof(ota_file), "%s_ota.bin", input_file);

    // 创建OTA文件
    FILE *ota = fopen(ota_file, "wb");
    if (ota == NULL) {
        perror("Failed to open file");
        fclose(bin_file);
        return;
    }

    // 写入OTA文件头
    fwrite(&header, sizeof(OTAHeader), 1, ota);

    // 写入固件镜像
    unsigned char buffer[1024];
    size_t bytes_read;
    while ((bytes_read = fread(buffer, 1, sizeof(buffer), bin_file)) > 0) {
        fwrite(buffer, 1, bytes_read, ota);
    }

    fclose(bin_file);
    fclose(ota);

    // 删除临时文件
    if (strcmp(temp_bin_file, input_file) != 0) {
        remove(temp_bin_file);
    }
}

// 将OTA升级包解包为bin镜像文件
void unpack_image(const char *input_file) {
    FILE *ota = fopen(input_file, "rb");
    if (ota == NULL) {
        perror("Failed to open file");
        return;
    }

    // 读取OTA文件头
    OTAHeader header;
    fread(&header, sizeof(OTAHeader), 1, ota);

    // 生成bin文件名
    char bin_file[256];
    char *dot = strrchr(input_file, '.');
    if (dot != NULL) {
        *dot = '\0';
    }
    snprintf(bin_file, sizeof(bin_file), "%s.bin", input_file);

    // 创建bin文件
    FILE *bin = fopen(bin_file, "wb");
    if (bin == NULL) {
        perror("Failed to open file");
        fclose(ota);
        return;
    }

    // 写入固件镜像
    unsigned char buffer[1024];
    size_t bytes_read;
    int remaining = header.firmware_length;
    while (remaining > 0 && (bytes_read = fread(buffer, 1, (remaining > sizeof(buffer) ? sizeof(buffer) : remaining), ota)) > 0) {
        fwrite(buffer, 1, bytes_read, bin);
        remaining -= bytes_read;
    }

    fclose(ota);
    fclose(bin);
}

// 查看OTA升级文件信息
void view_ota_info(const char *input_file) {
    FILE *ota = fopen(input_file, "rb");
    if (ota == NULL) {
        perror("Failed to open file");
        return;
    }

    // 读取OTA文件头
    OTAHeader header;
    fread(&header, sizeof(OTAHeader), 1, ota);

    // 输出信息
    printf("Identifier: 0x%02X 0x%02X\n", header.identifier1, header.identifier2);
    printf("Firmware Count: %d\n", header.firmware_count);
    printf("Firmware Version: %d.%d\n", header.major_version, header.minor_version);
    printf("Firmware Length: %d bytes\n", header.firmware_length);
    printf("Firmware Checksum: 0x%08X\n", header.checksum);
    printf("Firmware Type: %s\n", header.firmware_type);

    fclose(ota);
}

// 显示帮助信息
void show_help() {
    printf("Usage: otapack.exe [options] [arguments]\n");
    printf("Options:\n");
    printf("  -h           Show this help message\n");
    printf("  -p [file] [type] [version]  Pack image file to OTA upgrade package\n");
    printf("  -u [file]    Unpack OTA upgrade package to bin image file\n");
    printf("  -v [file]    View OTA upgrade file information\n");
    printf("Supported file formats: .hex, .bin, .cly\n");
    printf("Supported firmware types: Any ASCII string up to 10 bytes\n");
    printf("Supported firmware version format: a.b (a, b in range 0~255)\n");
}

int main(int argc, char *argv[]) {
    int opt;
    while ((opt = getopt(argc, argv, "hp:u:v:")) != -1) {
        switch (opt) {
            case 'h':
                show_help();
                break;
            case 'p':
                if (optind + 2 < argc) {
                    pack_image(optarg, argv[optind], argv[optind + 1]);
                } else {
                    fprintf(stderr, "Missing arguments for -p option\n");
                }
                break;
            case 'u':
                unpack_image(optarg);
                break;
            case 'v':
                view_ota_info(optarg);
                break;
            default:
                show_help();
                return 1;
        }
    }

    return 0;
}